<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
 * PHP version 5
 *
 * This source file is subject to version 2.1 of the GNU Lesser General Public
 * License, that is bundled with this package in the file COPYING, available
 * through the world wide web at the following URI:
 * http://www.gnu.org/copyleft/lesser.html.
 *
 * @package    ZNF
 * @subpackage Config
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: ModulesConfig.php 43 2007-06-26 23:36:35Z aronnax $
 * @since      Release 0.6.1
 * @link       http://www.zeronotice.org
 */

/**
 * <i>ZNF_Config_ModulesConfig</i> contains the collection of static module configuration informations.
 *
 * Returns the <i>ZNF_Config_ModulesConfig</i> object reference of a specified
 * module with a singleton pattern.
 *
 * @access     public
 * @package    ZNF
 * @subpackage Config
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: ModulesConfig.php 43 2007-06-26 23:36:35Z aronnax $
 * @since      Release 0.6.1
 * @link       http://www.zeronotice.org
 */
class ZNF_Config_ModulesConfig
{

    /**
     * The array of <i>ZNF_Config_ModulesConfig</i> object references.
     *
     * @access protected
     * @staticvar array $_modulesConfig
     */
    static protected $_modulesConfig;

    /**
     * The array of the configuration file.
     *
     * @access protected
     * @var array $_config
     */
    protected $_config;

    /**
     * Private constructor to avoid <i>ZNF_Config_ModulesConfig</i> object creation.
     *
     * @access private
     * @param array $appModuleConfig The module's application configuration
     */
    private function __construct($appModuleConfig)
    {
        $this->_parseConfiguration($appModuleConfig);
    }

    /**
     * Private clonator to avoid <i>ZNF_Config_ModulesConfig</i> object clonation.
     *
     * @access private
     */
    private function __clone()
    {
    }

    /**
     * Returns the reference to the <i>ZNF_Config_ModulesConfig</i> object with a singleton pattern.
     *
     * This object is shared by all the classes that require the module
     * configuration.
     *
     * @static
     * @access public
     * @param array $appModuleConfig
     * @return ZNF_Config_ModulesConfig
     */
    static public function getInstance($appModuleConfig)
    {
        if (!(isset(self::$_modulesConfig[$appModuleConfig['name']]))) {
            self::$_modulesConfig[$appModuleConfig['name']] = new ZNF_Config_ModulesConfig($appModuleConfig);
        }

        return self::$_modulesConfig[$appModuleConfig['name']];
    }

    /**
     * Parses and validates the configuration file.
     *
     * For performance reasons implements a caching mechanism that serializes the
     * configuration file in an internal structure.
     *
     * @access protected
     * @param array $appModuleConfig
     */
    protected function _parseConfiguration($appModuleConfig)
    {
        $serializedTimestamp = @filemtime(ZNF::CACHE_DIR . "/{$appModuleConfig['config']}.cache");
        $xmlTimestamp = filemtime(ZNF::CONFIG_DIR . "/{$appModuleConfig['config']}");

        if ($xmlTimestamp <= $serializedTimestamp) {
            $this->_config = unserialize(file_get_contents(ZNF::CACHE_DIR . "/{$appModuleConfig['config']}.cache"));
        } else {
            $configXml = new DOMDocument('1.0','utf-8');
            $configXml->load(ZNF::CONFIG_DIR . "/{$appModuleConfig['config']}");

            // quick hack to make ZNF work as a PEAR package, to be improved
            $dirName = dirname(__FILE__);
            $schemaPath = substr($dirName, 0, strpos($dirName, 'Config')) . "xsd/znf-module-config.xsd";
            if (!$configXml->schemaValidate($schemaPath)) {
                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgFileNotValid'));
            }

            $xpath = new DOMXPath($configXml);
            $xpath->registerNamespace('mod', 'http://www.zeronotice.org/ZNF/znf-module-config');
            $config = array();

            // get default action
            $default = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@default="true"]');

            if ($default->length == 1) {
                $forward = $default->item(0)->getAttribute('forward');
                $type    = $default->item(0)->getAttribute('type');

                // check if only one of forward or type attribute is defined
                if ($forward && $type) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardTypeOr'));
                }

                // check if forward is a valid action
                if ($forward && (!ereg('/', $forward) || ereg('//', $forward))) {

                    // TODO: add the checking of valid action in other module
                    if (!ereg('//', $forward)) {
                        $forwardAction = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $forward . '"]');

                        if (!$forwardAction->length) {
                            $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                            throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardAttributeValidAction'));
                        }
                    }
                }

                $config['action']['default']['forward']  = $forward;
                $config['action']['default']['include']  = $default->item(0)->getAttribute('include');
                $scope = $default->item(0)->getAttribute('scope');
                $name = $default->item(0)->getAttribute('name');

                // check if name is defined when scope is defined
                if ($scope && !$name) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgScopeDefinedNameValidFormBean'));
                }

                // check if name refers to a valid form-bean
                if ($name) {
                    $form = $xpath->query('/mod:znf-module-config/mod:form-beans/mod:form-bean[@name="' . $name . '"]');

                    if (!$form->length) {
                        $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                        throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgNameValidFormBean'));
                    }

                }

                $parameter = $default->item(0)->getAttribute('parameter');
                $parameterValue = $default->item(0)->getAttribute('parameterValue');

                // check if only one of forward or type attribute is defined
                if ($parameter && $parameterValue) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgParameterParameterValueOr'));
                }

                $config['action']['default']['parameter'] = $parameter;
                $config['action']['default']['parameterValue'] = $parameterValue;

                $config['action']['default']['scope']     = $scope;
                $config['action']['default']['name']      = $name;
                $config['action']['default']['nextPath']  = $default->item(0)->getAttribute('nextPath');
                $config['action']['default']['path']      = $default->item(0)->getAttribute('path');
                $input = $default->item(0)->getAttribute('input');
                $validate = $default->item(0)->getAttribute('validate');

                // check if validate and input are both defined
                if ($input && !$validate) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgInputValidateBoth'));
                } elseif (!$input && $validate) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgInputValidateBoth'));
                }

                $config['action']['default']['input']    = $input;
                $config['action']['default']['validate'] = $validate;
                $config['action']['default']['roles']    = $default->item(0)->getAttribute('roles');
                $config['action']['default']['type']     = $type;

                // get forward elements associated with the action
                $forwards = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $default->item(0)->getAttribute('path') . '"]/mod:forward');

                foreach ($forwards as $forward) {
                    $forwardPath = $forward->getAttribute('path');
                    $forwardRedirect = $forward->getAttribute('redirect');

                    // check if redirect is required and if the path is an action
                    if ($forwardRedirect == 'true') {

                        if ($forwardPath && ereg('/', $forwardPath) && !ereg('//', $forwardPath)) {
                            $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                            throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardElementRedirectTrueValidAction'));
                        }

                    }

                    // check if the path is a valid action
                    if ($forwardPath && (!ereg('/', $forwardPath) || ereg('//', $forwardPath))) {

                        // TODO: add the checking of valid action in other module
                        if (!ereg('//', $forwardPath)) {
                            $forwardAction = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $forwardPath . '"]');

                            if (!$forwardAction->length) {
                                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                                throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardElementValidAction'));
                            }
                        }
                    }

                    $forwardName = $forward->getAttribute('name');
                    $config['action']['default']['forwards-config'][$forwardName]['name']     = $forwardName;
                    $config['action']['default']['forwards-config'][$forwardName]['path']     = $forwardPath;
                    $config['action']['default']['forwards-config'][$forwardName]['redirect'] = $forwardRedirect;
                }

            // check if only one default action is defined
            } elseif ($default->length > 1) {
                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgOneActionDefault'));

            // check if at least one default action is defined
            } else {
                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgAtLeastOneActionDefault'));
            }

            // get global forwards
            $forwards = $xpath->query('/mod:znf-module-config/mod:global-forwards/mod:forward');

            foreach ($forwards as $forward) {
                $forwardPath = $forward->getAttribute('path');
                $forwardRedirect = $forward->getAttribute('redirect');

                // check if redirect is required and if the path is an action
                if ($forwardRedirect == 'true') {

                    if ($forwardPath && ereg('/', $forwardPath) && !ereg('//', $forwardPath)) {
                        $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                        throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardElementRedirectTrueValidAction'));
                    }

                }

                // check if the path is a valid action
                if ($forwardPath && (!ereg('/', $forwardPath) || ereg('//', $forwardPath))) {

                    // TODO: add the checking of valid action in other module
                    if (!ereg('//', $forwardPath)) {
                        $forwardAction = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $forwardPath . '"]');

                        if (!$forwardAction->length) {
                            $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                            throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardElementValidAction'));
                        }
                    }
                }

                $forwardName = $forward->getAttribute('name');
                $config['global-forwards'][$forwardName]['name']     = $forwardName;
                $config['global-forwards'][$forwardName]['path']     = $forward->getAttribute('path');
                $config['global-forwards'][$forwardName]['redirect'] = $forward->getAttribute('redirect');
            }

            // get all actions
            $actions = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action');

            foreach ($actions as $action) {
                $path    = $action->getAttribute('path');
                $forward = $action->getAttribute('forward');
                $type    = $action->getAttribute('type');

                // check if only one of forward or type attribute is defined
                if ($forward && $type) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardTypeOr'));
                }

                // check if forward is a valid action
                if ($forward && (!ereg('/', $forward) || ereg('//', $forward))) {

                    // TODO: add the checking of valid action in other module
                    if (!ereg('//', $forward)) {
                        $forwardAction = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $forward . '"]');

                        if (!$forwardAction->length) {
                            $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                            throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardAttributeValidAction'));
                        }
                    }
                }

                $config['action'][$path]['forward'] = $forward;
                $config['action'][$path]['include'] = $action->getAttribute('include');
                $scope = $action->getAttribute('scope');
                $name = $action->getAttribute('name');

                // check if name is defined when scope is defined
                if ($scope && !$name) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgScopeDefinedNameValidFormBean'));
                }

                // check if name refers to a valid form-bean
                if ($name) {
                    $form = $xpath->query('/mod:znf-module-config/mod:form-beans/mod:form-bean[@name="' . $name . '"]');

                    if (!$form->length) {
                        $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                        throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgNameValidFormBean'));
                    }

                }

                $parameter = $action->getAttribute('parameter');
                $parameterValue = $action->getAttribute('parameterValue');

                // check if only one of forward or type attribute is defined
                if ($parameter && $parameterValue) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgParameterParameterValueOr'));
                }

                $config['action'][$path]['parameter'] = $parameter;
                $config['action'][$path]['parameterValue'] = $parameterValue;

                $config['action'][$path]['scope']     = $scope;
                $config['action'][$path]['name']      = $name;
                $config['action'][$path]['nextPath']  = $action->getAttribute('nextPath');
                $config['action'][$path]['path']      = $action->getAttribute('path');
                $input = $action->getAttribute('input');
                $validate = $action->getAttribute('validate');

                // check if validate and input are both defined
                if ($input && !$validate) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgInputValidateBoth'));
                } elseif (!$input && $validate) {
                    $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                    throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgInputValidateBoth'));
                }

                $config['action'][$path]['input']    = $input;
                $config['action'][$path]['validate'] = $validate;
                $config['action'][$path]['roles']    = $action->getAttribute('roles');
                $config['action'][$path]['type']     = $type;

                // get forward elements associated with the action
                $forwards = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $path . '"]/mod:forward');

                foreach ($forwards as $forward) {
                    $forwardPath = $forward->getAttribute('path');
                    $forwardRedirect = $forward->getAttribute('redirect');

                    // check if redirect is required and if the path is an action
                    if ($forwardRedirect == 'true') {

                        if ($forwardPath && ereg('/', $forwardPath) && !ereg('//', $forwardPath)) {
                            $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                            throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardElementRedirectTrueValidAction'));
                        }

                    }

                    // check if the path is a valid action
                    if ($forwardPath && (!ereg('/', $forwardPath) || ereg('//', $forwardPath))) {

                        // TODO: add the checking of valid action in other module
                        if (!ereg('//', $forwardPath)) {
                            $forwardAction = $xpath->query('/mod:znf-module-config/mod:action-mappings/mod:action[@path="' . $forwardPath . '"]');

                            if (!$forwardAction->length) {
                                $translation = new ZNF_Presentation_Translation('ZNF', $_SESSION['znf']['lang']);
                                throw new ZNF_Config_ModulesConfigException($translation->get('errorModCfgForwardElementValidAction'));
                            }
                        }
                    }

                    $forwardName = $forward->getAttribute('name');
                    $config['action'][$path]['forwards-config'][$forwardName]['name']     = $forwardName;
                    $config['action'][$path]['forwards-config'][$forwardName]['path']     = $forwardPath;
                    $config['action'][$path]['forwards-config'][$forwardName]['redirect'] = $forwardRedirect;
                }
            }

            // get form beans
            $forms = $xpath->query('/mod:znf-module-config/mod:form-beans/mod:form-bean');

            foreach ($forms as $form) {
                $formName = $form->getAttribute('name');
                $config['form-bean'][$formName]['name'] = $formName;
                $config['form-bean'][$formName]['type'] = $form->getAttribute('type');
            }

            file_put_contents(ZNF::CACHE_DIR . "/{$appModuleConfig['config']}.cache", serialize($config));
            $this->_config = $config;
        }
    }

    /**
     * Returns the global forwards configuration.
     *
     * @access public
     * @return array
     */
    public function getGlobalForwardsConfig()
    {
        if (array_key_exists('global-forwards', $this->_config)) {
            return $this->_config['global-forwards'];
        }

        return null;
    }

    /**
     * Returns the action configuration for the defined <i>path</i>.
     *
     * @access public
     * @param string $path
     * @return array
     */
    public function findActionConfig($path)
    {
        if ($path && array_key_exists($path, $this->_config['action'])) {
            // extracts the configuration of the action requested
            return $this->_config['action'][$path];
        }

        return null;
    }

    /**
     * Returns the form bean configuration for the defined <i>name</i>.
     *
     * @access public
     * @param string $name
     * @return array
     */
    public function findFormBeanConfig($name)
    {
        if ($name && array_key_exists($name, $this->_config['form-bean'])) {
            // extracts the configuration of the action requested
            return $this->_config['form-bean'][$name];
        }

        return null;
    }

    /**
     * Destroys the <i>ZNF_Config_ModulesConfig</i> object.
     *
     * @access public
     */
    public function __destruct()
    {
    }

}

/**
 * <i>ZNF_Config_ModulesConfigException</i> is the exception type for the <i>ZNF_Config_ModulesConfig</i> class.
 *
 * <i>ZNF_Config_ModulesConfigException</i> extends the <i>Exception</i> class of PHP5.
 *
 * @access     public
 * @package    ZNF
 * @subpackage Config
 * @author     Alessandro Rossini <http://www.alessandrorossini.org>
 * @author     Graziano Liberati <http://www.liberati.org>
 * @copyright  2004-2007 The ZNF Development Team
 * @license    LGPL License 2.1 <http://www.gnu.org/copyleft/lesser.html>
 * @version    SVN $Id: ModulesConfig.php 43 2007-06-26 23:36:35Z aronnax $
 * @since      Release 0.6.1
 * @link       http://www.zeronotice.org
 */
class ZNF_Config_ModulesConfigException extends Exception
{
}

?>
